#include "include/muduo/proto/codec.h"
#include "include/muduo/proto/dispatcher.h"
#include "protocol.pb.h"

#include "muduo/base/Logging.h"
#include "muduo/base/Mutex.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"

#include <iostream>
#include <memory>
#include <functional>
#include <string>
#include <unordered_map>

class PbServer
{
public:
    using TransReqPtr = std::shared_ptr<notbean::TranslateRequest>;
    using AddReqPtr = std::shared_ptr<notbean::AddRequest>;
public:
    PbServer(uint16_t port)
        : _server(&_baseLoop, muduo::net::InetAddress("0.0.0.0", port), "PbServer", muduo::net::TcpServer::kReusePort)
        , _dispatcher(std::bind(&PbServer::onUnknownMessage, this, 
            std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))
        , _codec(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher, 
            std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))
    {
        _dispatcher.registerMessageCallback<notbean::TranslateRequest>(std::bind(&PbServer::onTranslate, this, 
            std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        _dispatcher.registerMessageCallback<notbean::AddRequest>(std::bind(&PbServer::onAdd, this, 
            std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        _server.setConnectionCallback(std::bind(&PbServer::onCinnection, this,
            std::placeholders::_1));
        _server.setMessageCallback(std::bind(&ProtobufCodec::onMessage, &_codec, 
            std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
    }

    void start() 
    {
        _server.start();
        _baseLoop.loop();
    }

    ~PbServer()
    {}
private:
    void onCinnection(const muduo::net::TcpConnectionPtr& conn)
    {
        if(conn->connected()) std::cout << "connection established..." << std::endl;
        else std::cout << "connection closed..." << std::endl;
    }

    void onUnknownMessage(const muduo::net::TcpConnectionPtr& conn, const MessagePtr& message, muduo::Timestamp timestamp)
    {
        std::cout << "unknown message: " << message->GetTypeName() << std::endl;
        conn->shutdown();
    }

    void onTranslate(const muduo::net::TcpConnectionPtr& conn, const TransReqPtr& message, muduo::Timestamp timestamp)
    {
        std::string msg = message->msg();
        notbean::TranslateResponse resp;
        resp.set_msg(doTranslate(msg));
        _codec.send(conn, resp); //进行序列化，调用conn->send()
    }

    void onAdd(const muduo::net::TcpConnectionPtr& conn, const AddReqPtr& message, muduo::Timestamp timestamp)
    {
        int op1 = message->op1();
        int op2 = message->op2();
        notbean::AddResponse resp;
        resp.set_result(doAadd(op1, op2));
        _codec.send(conn, resp);
    }

    std::string doTranslate(const std::string& word)
    {
        static std::unordered_map<std::string, std::string> map = {
            {"int", "整形"}, {"string", "字符串"}, {"heap", "堆"}, {"hash", "哈希"}
        };
        auto it = map.find(word);
        if(it == map.end()) return "翻译失败";
        return it->second;
    }

    int doAadd(int op1, int op2)
    {
        return op1 + op2;
    }
private:
    muduo::net::EventLoop _baseLoop;
    muduo::net::TcpServer _server; 
    ProtobufDispatcher _dispatcher; //请求分发器
    ProtobufCodec _codec; //pb协议处理器  codec: Coder-Decoder 编解码
};

int main()
{
    std::unique_ptr<PbServer> svr = std::make_unique<PbServer>(9090);
    svr->start();

    return 0;
}